package com.ruoyi.common.swagger.config.plugin;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.TypeResolver;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.ruoyi.common.swagger.annotation.ApiModelPropertyEnum;
import io.swagger.annotations.ApiModelProperty;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import springfox.documentation.schema.Annotations;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.spring.web.DescriptionResolver;
import springfox.documentation.swagger.common.SwaggerPluginSupport;
import springfox.documentation.swagger.schema.ApiModelProperties;

import java.lang.reflect.*;
import java.util.Map;
import java.util.Optional;

/**
 * @author liyang
 * @description
 * @date 2022-10-10 11:18:25
 */
@Component
@Order(-2147482648)
public class YouYaModelPropertyBuilderPlugin implements ModelPropertyBuilderPlugin {

    private final DescriptionResolver descriptions;

    @Autowired
    public YouYaModelPropertyBuilderPlugin(DescriptionResolver descriptions) {
        this.descriptions = descriptions;
    }

    @Override
    public void apply(ModelPropertyContext context) {
        Optional<ApiModelProperty> annotation = Optional.empty();
        if (context.getAnnotatedElement().isPresent()) {
            annotation = (Optional) annotation.map(Optional::of).orElse(ApiModelProperties.findApiModePropertyAnnotation((AnnotatedElement) context.getAnnotatedElement().get()));
        }

        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = (Optional) annotation.map(Optional::of).orElse(Annotations.findPropertyAnnotation((BeanPropertyDefinition) context.getBeanPropertyDefinition().get(), ApiModelProperty.class));
        }

        if (annotation.isPresent()) {
            ApiModelProperty apiModelProperty = annotation.get();
            ApiModelPropertyEnum apiModelPropertyEnum = context.getBeanPropertyDefinition().get().getField().getAnnotation(ApiModelPropertyEnum.class);
            try {
                StringBuilder stringBuilder = null;
                if (apiModelPropertyEnum != null && !apiModelProperty.value().contains("枚举")) {
                    InvocationHandler invocationHandler = Proxy.getInvocationHandler(apiModelProperty);
                    Field memberValues = invocationHandler.getClass().getDeclaredField("memberValues");
                    memberValues.setAccessible(true);
                    Map o = (Map) memberValues.get(invocationHandler);
                    Class<?> aClass = apiModelPropertyEnum.value();
                    Object[] enumConstants = aClass.getEnumConstants();
                    stringBuilder = new StringBuilder();
                    stringBuilder.append(apiModelProperty.value());
                    stringBuilder.append("枚举(");
                    for (Object enumConstant : enumConstants) {
                        Method[] declaredMethods = aClass.getDeclaredMethods();
                        for (Method declaredMethod : declaredMethods) {
                            String name = declaredMethod.getName();
                            if (name.equals("values") || name.equals("valueOf")) {
                                continue;
                            }
                            stringBuilder.append(declaredMethod.invoke(enumConstant));
                            stringBuilder.append(":");
                        }
                        stringBuilder.delete(stringBuilder.length() - 1, stringBuilder.length());
                        stringBuilder.append(",");
                    }
                    stringBuilder.delete(stringBuilder.length() - 1, stringBuilder.length());
                    stringBuilder.append(")");
                    o.put("value", stringBuilder.toString());
                }
                context.getBuilder()
                        .allowableValues(apiModelProperty.allowableValues() == null ? null : ApiModelProperties.allowableValueFromString(apiModelProperty.allowableValues()))
                        .required(apiModelProperty.required())
                        .readOnly(apiModelProperty.readOnly())
                        .description(stringBuilder == null ? null : stringBuilder.toString())
                        .isHidden(apiModelProperty.hidden())
                        .type(resolve(context.getResolver(), apiModelProperty))
                        .position(apiModelProperty.position());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public ResolvedType resolve(TypeResolver resolver, ApiModelProperty apiModelProperty) {
        try {
            return resolver.resolve(Class.forName(apiModelProperty.dataType()), new Type[0]);
        } catch (ClassNotFoundException e) {
            return resolver.resolve(Object.class, new Type[0]);
        }

    }

    @Override
    public boolean supports(DocumentationType delimiter) {
        return SwaggerPluginSupport.pluginDoesApply(delimiter);
    }
}
